#define TMP_CONNECTION "tmp_connection"

#include <QDateTime>
#include <QUuid>

#include <QtCrypto>
#include <QtXml>

#include "cdatabase.h"
#include "src/centry.h"

CDatabase::CDatabase(QString name): m_filename(name)
{

}

CDatabase::~CDatabase()
{
    QSqlDatabase::database().close();
}

bool CDatabase::createDatabase(QString filename, QString cipher, QString key)
{
    QSqlDatabase::addDatabase("QSQLITE", TMP_CONNECTION);

    if(!QSqlDatabase::database(TMP_CONNECTION).isValid())
    {
        debug("Can't load database driver");
        return false;
    }

    debug("connection created");

    if(QFile::exists(filename))
        QFile::resize(filename, 0);

    QSqlDatabase::database(TMP_CONNECTION).setDatabaseName(filename);

    if(!QSqlDatabase::database(TMP_CONNECTION).open())
    {
        debug("Can't open database. Filename: " + filename
              + ". Reason: " + QSqlDatabase::database(TMP_CONNECTION).lastError().driverText());
        QSqlDatabase::removeDatabase(QSqlDatabase::database(TMP_CONNECTION).connectionName());
    }
    else
    {
        debug("Driver loaded, file created");
        QFile file(":/new_db.sql");
        if(!file.open(QIODevice::ReadOnly))
        {
            debug("Can't open scheme for new database");
            return false;
        }
        QString querystr = QString::fromLatin1(file.readAll());

        foreach(QString str, querystr.split(";"))
        {
            QSqlQuery query(QSqlDatabase::database(TMP_CONNECTION));
            if(!query.exec(str))
            {
                debug("Create table: " + query.lastQuery());
                debug("Can't exec query. Reason: " + QSqlDatabase::database(TMP_CONNECTION).lastError().driverText());
                return false;
            }
        }

        QSqlQuery query(QSqlDatabase::database(TMP_CONNECTION));
        query.prepare("INSERT INTO `properties` (`name`, `value`) "
                      "VALUES ('cipher', '" + cipher + "')");
        if(!query.exec())
        {
            debug("Query: " + query.lastQuery());
            debug("Can't exec query. Reason: " + QSqlDatabase::database(TMP_CONNECTION).lastError().driverText());
            return false;
        }

        query.finish();

        query.prepare("INSERT INTO `properties` (`name`, `value`) "
                      "VALUES ('md5', '" + genMd5(key) + "')");
        if(!query.exec())
        {
            debug("Query: " + query.lastQuery());
            debug("Can't exec query. Reason: " + QSqlDatabase::database(TMP_CONNECTION).lastError().driverText());
            return false;
        }
        query.finish();

        query.prepare(QString("INSERT INTO `properties` (`name`, `value`) "
                      "VALUES ('last_sync', '%1')").arg("0000-00-00T00:00:00"));
        if(!query.exec())
        {
            debug("Query: " + query.lastQuery());
            debug("Can't exec query. Reason: " + QSqlDatabase::database(TMP_CONNECTION).lastError().driverText());
            return false;
        }

        debug("Database successfully created.");
    }

    QSqlDatabase::removeDatabase(TMP_CONNECTION);

    return true;
}

QByteArray CDatabase::encrypt_array(QByteArray data)
{
    QCA::SymmetricKey enc_key(QCA::getProperty("key").toByteArray());
    QCA::Hash md5("md5");
    QCA::SecureArray iv = md5.process(QCA::getProperty("key").toByteArray());
    QCA::Cipher cipher(QCA::getProperty("cipher").toString(), QCA::Cipher::CBC, QCA::Cipher::DefaultPadding,
                       QCA::Encode, enc_key, iv);

    QCA::SecureArray crypt = cipher.process(data);
    
    return crypt.toByteArray();
}

QByteArray CDatabase::decrypt_array(QByteArray data)
{
    QCA::SymmetricKey key(QCA::getProperty("key").toByteArray());
    QCA::Hash md5("md5");
    QCA::SecureArray iv = md5.process(QCA::getProperty("key").toByteArray());
    QCA::Cipher cipher(QCA::getProperty("cipher").toString(), QCA::Cipher::CBC, QCA::Cipher::DefaultPadding,
                       QCA::Decode, key, iv);

    QCA::SecureArray crypt = cipher.process(data);

    return crypt.toByteArray();
}

QString CDatabase::genMd5(QString str)
{
    QCA::Hash md5("md5");
    QByteArray arr = md5.process(str.toLatin1()).toByteArray().toHex();

    return QString::fromLatin1(arr);
}

QVariant CDatabase::getSectionAttr(QString id, QString name)
{
    if(name=="name" && id==0)
        return QT_TRANSLATE_NOOP("CDatabase", "Root");

    QVariant result;
    if(!open())
        return result;

    QSqlQuery query(QSqlDatabase::database());

    query.prepare(QString("SELECT `%1` FROM `sections` WHERE `id`='%2' LIMIT 1")
                  .arg(name).arg(id));

    if(!query.exec())
        return QVariant();

    if(!query.first())
            return QVariant();

    result = query.value(0);
    query.finish();
    return result;
}

QMap<QString, QVariant> CDatabase::getSection(QString id)
{
    QMap<QString, QVariant> result;
    if(!open())
        return result;

    QSqlQuery query(QSqlDatabase::database());

    query.prepare(QString("SELECT * FROM `sections` WHERE `id`='%1' LIMIT 1").arg(id));

    if(!query.exec())
        return result;

    if(!query.first())
            return result;

    QSqlRecord record = query.record();
    for(int i=0; i<record.count(); i++)
        result.insert(record.fieldName(i), query.value(i));

    query.finish();

    result.insert("entries", getSectionEntriesCount(id));

    return result;
}

void CDatabase::close()
{
    QString connection = QSqlDatabase::database().connectionName();
    if(QSqlDatabase::database(connection).isValid())
    {
        QSqlDatabase::database().close();
        QSqlDatabase::removeDatabase(connection);
        debug("Database is valid: " + QString::number(QSqlDatabase::database(connection).isValid()));
        debug("Connections: " + QSqlDatabase::connectionNames().join(", "));
    }
}

bool CDatabase::open(bool del_old)
{
    if(m_filename=="")
    {
        debug("Filename doesn't set. Exitting.");
        return false;
    }

    if(del_old)
    {
 /*       QString connection = QSqlDatabase::database().connectionName();
        if(QSqlDatabase::database(connection).isValid())
        {
            QSqlDatabase::database().close();
            QSqlDatabase::removeDatabase(connection);
            debug("Database is valid: " + QString::number(QSqlDatabase::database(connection).isValid()));
            debug("Connections: " + QSqlDatabase::connectionNames().join(", "));
        }*/
        close();
    }

    if(!QSqlDatabase::database().isValid())
    {
        QSqlDatabase::addDatabase("QSQLITE");
        debug("Database added");
        if(!QSqlDatabase::database().isValid())
            return false;
    }

    if(QSqlDatabase::database().isOpen())
        return true;

    QSqlDatabase::database().setDatabaseName(m_filename);

    debug("Filename setted");

    bool result = QSqlDatabase::database().open();
    if(!result)
        debug("Can't open database. Filename: " + m_filename + ". Reason: " +
              QSqlDatabase::database().lastError().driverText());
    else
    {
        QSqlQuery query(QSqlDatabase::database());
        query.prepare("SELECT `value` FROM `properties` WHERE `name`='cipher'");
        if(query.exec() && query.first())
        {
            QString cipher = query.value(0).toString();
            debug("Database cipher: " + cipher);
            QCA::setProperty("cipher", cipher);
        }
        else
        {
            debug("Can't detect cipher of database. Will set default aes256");
            QCA::setProperty("cipher", "aes256");
        }
        query.finish();

        query.prepare("SELECT `value` FROM `properties` WHERE `name`='md5'");
        if(query.exec() && query.first())
        {
            QString md5 = query.value(0).toString();
            debug("Database md5: " + md5);
            QCA::setProperty("md5", md5);
        }
        else
            debug("Can't detect md5 of database. Will be undefined");
        query.finish();

        debug("Database successfully opened.");
    }

    return result;
}

bool CDatabase::isNew()
{
    if(!open())
        return false;

    QSqlQuery query(QSqlDatabase::database());

    query.prepare("SELECT `value` FROM `properties` WHERE `name`='md5'");

    if(!query.exec())
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().driverText());
        return false;
    }

    return true;
}

QString CDatabase::getKeyMd5()
{
    if(!open())
        return QString();

    QSqlQuery query(QSqlDatabase::database());
    query.prepare("SELECT `value` FROM `properties` WHERE `name`='md5'");
    if(query.exec() && query.first())
    {
        QString md5 = query.value(0).toString();
        debug("Key md5: " + md5);
        return md5;
    }

    debug("Can't detect md5 of key. Will be undefined.");
    return QString();
}

QList<QString> CDatabase::getSectionChildern(QString id)
{
    QList<QString> result;

    if(!open())
        return result;

    QSqlQuery query(QSqlDatabase::database());

    query.prepare(QString("SELECT `id` FROM `sections` WHERE `parent`='%1'").arg(id));

    if(!query.exec())
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().driverText());
        return result;
    }

    while(query.next())
        result.append(query.value(0).toString());

    query.finish();

    return result;
}

QString CDatabase::getTableByType(int type)
{
    switch(type)
    {
        case Section: return "sections";
        case Entry: return "entries";
        case Field: return "fields";
    }

    return QString();
}

int CDatabase::getTypeByTable(QString str)
{
    if(str=="sections")
        return this->Section;

    else if(str=="entries")
        return this->Entry;

    else // if(str=="fields")
        return this->Field;
}

QStringList CDatabase::getSectionBlobs()
{
    QStringList result;

//    if(m_map.contains(CTreeItem::getStringById(CTreeItem::Icon)))
        result << "*" + CTreeItem::getStringById(CTreeItem::Icon);

//    if(m_map.contains(CTreeItem::getStringById(CTreeItem::Name)))
        result << CTreeItem::getStringById(CTreeItem::Name);

    return result;
}

QStringList CDatabase::getEntryBlobs()
{
    QStringList result;

//    if(m_map.contains(CEntry::getStringById(CEntry::Icon)))
        result << CEntry::getStringById(CEntry::Icon);

//    if(m_map.contains(CEntry::getStringById(CEntry::Login)))
        result << CEntry::getStringById(CEntry::Login);

//    if(m_map.contains(CEntry::getStringById(CEntry::Password)))
        result << CEntry::getStringById(CEntry::Password);

//    if(m_map.contains(CEntry::getStringById(CEntry::Comments)))
        result << CEntry::getStringById(CEntry::Comments);

//    if(m_map.contains(CEntry::getStringById(CEntry::Name)))
        result << CEntry::getStringById(CEntry::Name);

    return result;
}

QStringList CDatabase::getFieldBlobs()
{
    QStringList result;

    result << "name" << "value";

    return result;
}

QStringList CDatabase::getBlobsByType(int type)
{
    switch(type)
    {
        case Section: return getSectionBlobs();
        case Entry: return getEntryBlobs();
        case Field: return getFieldBlobs();
    }

    return QStringList();
}

bool CDatabase::add(QMap<QString, QVariant> map, QString table, bool encrypted)
{
    if(!open())
        return false;

    QSqlQuery query(QSqlDatabase::database());

    QStringList blobs = getBlobsByType(getTypeByTable(table));

    QString querystr = "INSERT INTO `" + table + "` \n(";
    QString fields;
    QString values;
    bool flag = false;
    foreach(QString key, map.keys())
    {
        if(flag)
        {
            fields += ", ";
            values += ", ";
        }
        else
            flag = true;

        fields += "`" + key + "` ";
        if(blobs.contains(key))
        {
            QByteArray blob;
            if(encrypted)
            {
                blob = map.value(key).toByteArray();
            }
            else
            {
                if(key.at(0)=='*')
                {
                    key.remove(0, 1);
                    blob = map.value(key).toByteArray().toHex();
                }
                else
                    blob = encrypt_array(map.value(key).toByteArray()).toHex();
            }

            values += "X'" + QString(blob.data()) + "' ";
        }
        else
            values += "'" + map.value(key).toString() + "' ";
    }

    querystr += fields + ") VALUES \n(" + values + ")";

    debug(querystr);

    if(!query.exec(querystr))
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().driverText());
        return false;
    }

    debug("Updated successfully");

    query.finish();
    return addChange(map.value("id").toString(), getTypeByTable(table), this->Add);
}

bool CDatabase::update(QMap<QString, QVariant> map, QString id, QString table, bool encrypted)
{
    if(!open())
        return false;

    QSqlQuery query(QSqlDatabase::database());

    QStringList blobs = getBlobsByType(getTypeByTable(table));

    QString querystr = "UPDATE `" + table + "` SET ";
    bool flag = false;
    foreach(QString key, map.keys())
    {
        if(flag)
            querystr += ", ";
        else
            flag = true;

        querystr += "`" + key + "`=";
        if(blobs.contains(key))
        {
            QByteArray blob;
            if(encrypted)
            {
                blob = map.value(key).toByteArray();
            }
            else
            {
                if(key.at(0)=='*')
                {
                    key.remove(0, 1);
                    blob = map.value(key).toByteArray().toHex();
                }
                else
                    blob = encrypt_array(map.value(key).toByteArray()).toHex();
            }
            querystr += "X'" + QString(blob.data()) + "' ";
        }
        else
            querystr += "'" + map.value(key).toString() + "' ";
    }

    querystr += "WHERE `id`='" + id + "'";

    debug(querystr);
    if(!query.exec(querystr))
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().driverText());
        return false;
    }

    debug("Updated successfully");

    query.finish();
    return addChange(id, getTypeByTable(table), this->Edit);
}

int CDatabase::getSectionEntriesCount(QString id)
{
    int result = 0;

    if(!open())
        return result;

    QSqlQuery query(QSqlDatabase::database());

    query.prepare(QString("SELECT COUNT(*) FROM `entries` WHERE `parent`='%1'").arg(id));

    if(!query.exec())
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().driverText());
        return result;
    }

    query.first();

    result = query.value(0).toInt();

    query.finish();

    return result;
}

bool CDatabase::addSection(QMap<QString, QVariant> map)
{
    return add(map, getTableByType(this->Section));
}

bool CDatabase::updateSection(QMap<QString, QVariant> map, QString id)
{
    return update(map, id, getTableByType(this->Section));
}

bool CDatabase::deleteSection(QString id)
{
    QSqlQuery query(QSqlDatabase::database());
    query.prepare(QString("SELECT `id` FROM `sections` WHERE `parent`='%1'").arg(id));
    if(!query.exec())
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().driverText());
        return false;
    }

    while(query.next())
    {
        QString child = query.value(0).toString();
        deleteSection(child);
    }

    query.finish();

    query.prepare(QString("SELECT `id` FROM `entries` WHERE `parent`='%1'").arg(id));
    if(!query.exec())
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().driverText());
        return false;
    }

    while(query.next())
    {
        QString child = query.value(0).toString();
        deleteEntry(child);
    }

    query.finish();

    query.prepare(QString("DELETE FROM `sections` WHERE `id`='%1'").arg(id));
    if(!query.exec())
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().driverText());
        return false;
    }

    debug("Section with id = " + id + " deleted successfully");

    return addChange(id, this->Section, this->Delete);
}

QList<QString> CDatabase::getEntriesList(QString id)
{
    QList<QString> result;

    if(!open())
        return result;

    QSqlQuery query(QSqlDatabase::database());

    query.prepare(QString("SELECT `id` FROM `entries` WHERE `parent`='%1'").arg(id));

    if(!query.exec())
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().driverText());
        return result;
    }

    while(query.next())
        result.append(query.value(0).toString());

    query.finish();

    return result;
}

QMap<QString, QVariant> CDatabase::getEntry(QString id)
{
    QMap<QString, QVariant> result;
    if(!open())
        return result;

    QSqlQuery query(QSqlDatabase::database());

    query.prepare(QString("SELECT * FROM `entries` WHERE `id`='%1' LIMIT 1").arg(id));

    if(!query.exec())
        return result;

    if(!query.first())
            return result;

    QSqlRecord record = query.record();
    for(int i=0; i<record.count(); i++)
        result.insert(record.fieldName(i), query.value(i));

    query.finish();

    result.insert("fields", getEntryFieldsCount(id));

    return result;
}

int CDatabase::getEntryFieldsCount(QString id)
{
    int result = 0;

    if(!open())
        return result;

    QSqlQuery query(QSqlDatabase::database());

    query.prepare(QString("SELECT COUNT(*) FROM `fields` WHERE `entry_id`='%1'").arg(id));

    if(!query.exec())
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().driverText());
        return result;
    }

    query.first();

    result = query.value(0).toInt();

    query.finish();

    return result;
}

QList<QMap<QString, QVariant> > CDatabase::getEntryFields(QString id)
{
    QList<QMap<QString, QVariant> > result;
    if(!open())
        return result;

    QSqlQuery query(QSqlDatabase::database());

    query.prepare(QString("SELECT * FROM `fields` WHERE `entry_id`='%1'").arg(id));

    if(!query.exec())
        return result;

    while(query.next())
    {
        QMap<QString, QVariant> map;
        QSqlRecord record = query.record();
        for(int i=0; i<record.count(); i++)
            map.insert(record.fieldName(i), query.value(i));

        result.append(map);
    }

    query.finish();

    return result;
}

/*bool CDatabase::addField(QString entry_id, QVariant name, int type, QVariant value, int order)
{
    QSqlQuery query(QSqlDatabase::database());

    QByteArray blob_value = encrypt_array(value.toByteArray()).toHex();
    QByteArray blob_name = encrypt_array(name.toByteArray()).toHex();

    QCryptographicHash hash(QCryptographicHash::Md5);
    QUuid salt = QUuid::createUuid();
    QString str = QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz") + salt.toString();
    hash.addData(str.toLatin1());
    QByteArray arr = hash.result();

    QString id =  QString(arr.toHex()).toUpper();

    query.prepare(QString("INSERT INTO `fields` (`id`, `entry_id`, `name`, `type`, `order`, `value`) "
                      "VALUES ('%1', '%2', X'%3', '%4', '%5', X'%6')")
              .arg(id).arg(entry_id).arg(blob_name.data()).arg(type).arg(order).arg(blob_value.data()));

    if(!query.exec())
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().databaseText());
        return false;
    }

    debug("Successfully inserted.");

    query.finish();

    return addChange(id, this->Field, this->Add);
}*/

/*bool CDatabase::updateField(QString id, QVariant name, int type, QVariant value, int order)
{
    QSqlQuery query(QSqlDatabase::database());

    QByteArray blob_value = encrypt_array(value.toByteArray()).toHex();
    QByteArray blob_name = encrypt_array(name.toByteArray()).toHex();

    query.prepare(QString("UPDATE `fields` SET "
                          "`name`=X'%1', "
                          "`type`='%2', "
                          "`order`='%3', "
                          "`value`=X'%4' "
                          "WHERE `id`='%5'")
              .arg(blob_name.data()).arg(type).arg(order).arg(blob_value.data()).arg(id));

    if(!query.exec())
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().databaseText());
        return false;
    }
    debug("Query: " + query.lastQuery());

    debug("Successfully updated.");

    query.finish();

    return addChange(id, this->Field, this->Edit);
}*/

bool CDatabase::addField(QMap<QString, QVariant> map)
{
    return add(map, getTableByType(this->Field));
}

bool CDatabase::updateField(QMap<QString, QVariant> map, QString id)
{
    return update(map, id, getTableByType(this->Field));
}

bool CDatabase::deleteField(QString id)
{
    QSqlQuery query(QSqlDatabase::database());
    query.prepare(QString("DELETE FROM `fields` WHERE `id`='%1'").arg(id));
    if(!query.exec())
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().driverText());
        return false;
    }

    query.finish();

    debug("Field with id='" + id + "' deleted successfully");

    return addChange(id, this->Field, this->Delete);
}

bool CDatabase::addEntry(QMap<QString, QVariant> map)
{
   return add(map, getTableByType(this->Entry));
}

bool CDatabase::deleteEntry(QString id)
{
    QSqlQuery query(QSqlDatabase::database());
    query.prepare(QString("DELETE FROM `entries` WHERE `id`='%1'").arg(id));
    if(!query.exec())
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().driverText());
        return false;
    }

    query.finish();

    query.prepare(QString("DELETE FROM `fields` WHERE `entry_id`='%1'").arg(id));
    if(!query.exec())
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().driverText());
        return false;
    }

    query.finish();

    debug("Entry with id='" + id + "' deleted successfully");

    return addChange(id, this->Entry, this->Delete);
}

bool CDatabase::updateEntry(QMap<QString, QVariant> map, QString id)
{
    return update(map, id, getTableByType(this->Entry));
}

bool CDatabase::addChange(QString entry_id, int entry_type, int action_type)
{
    QSqlQuery query(QSqlDatabase::database());

    query.prepare(QString("DELETE FROM `changes` WHERE `entry_id`='%1'").arg(entry_id));
    query.exec();
    query.finish();

    query.prepare(QString("INSERT INTO `changes` (`entry_id`, `entry_type`, `action_type`, `date`) "
                      "VALUES ('%1', '%2', '%3', '%4')")
              .arg(entry_id).arg(entry_type).arg(action_type).arg(QDateTime::currentDateTime().toString(Qt::ISODate)));

    if(!query.exec())
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().databaseText());
        return false;
    }

    debug("Change query: " + query.lastQuery());

    query.finish();

    debug("Change successfully inserted");

    return true;
}

QList<CChange> CDatabase::changes()
{
    QList<CChange> result;

    QSqlQuery query(QSqlDatabase::database());

    if(!query.exec("SELECT * FROM `changes`"))
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().databaseText());
        return result;
    }

    QDateTime last_sync = QDateTime::fromString(lastSync(), Qt::ISODate);
    while(query.next())
    {
        CChange c;

        QSqlRecord record = query.record();
        for(int i=0; i<record.count(); i++)
        {
            if(record.fieldName(i)=="entry_id")
                c.id = query.value(i).toString();
            else if(record.fieldName(i)=="entry_type")
                c.type = query.value(i).toInt();
            else if(record.fieldName(i)=="action_type")
                c.action = query.value(i).toInt();
            else if(record.fieldName(i)=="date")
                c.datetime = query.value(i).toString();
        }

        QDateTime tmp = QDateTime::fromString(c.datetime, Qt::ISODate);
        if(tmp>=last_sync)
            result.append(c);
    }

    query.finish();

    return result;
}

QList<ChangeMap*> CDatabase::changedEntries()
{
    QList<ChangeMap*> result;

    if(!open())
        return result;

    QSqlQuery query(QSqlDatabase::database());

    query.prepare(QString("SELECT `entry_id`,`date` FROM `changes` WHERE `entry_type`='%1' "
                           "AND (`action_type`='%2' OR `action_type`='%3')")
        .arg(this->Entry).arg(this->Add).arg(this->Edit));

    if(!query.exec())
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().databaseText());
        return result;
    }

    QDateTime last_sync = QDateTime::fromString(lastSync(), Qt::ISODate);
    QStringList entries;
    while(query.next())
    {
        QDateTime tmp = QDateTime::fromString(query.value(1).toString(), Qt::ISODate);
        if(tmp>=last_sync)
            entries.append(query.value(0).toString());
    }

    query.finish();

    if(!entries.count())
        return result;

    query.prepare("SELECT * FROM `entries` WHERE `id`='" + entries.join("' OR `id`='") + "'");
    if(!query.exec())
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().databaseText());
        return result;
    }

    while(query.next())
    {
        ChangeMap* c = new ChangeMap();

        QSqlRecord record = query.record();
        for(int i=0; i<record.count(); i++)
        {
            QString f = record.fieldName(i);
            if(f == "name" || f == "icon" || f == "comments" || f == "login" || f == "password")
            {
                QByteArray blob = query.value(i).toByteArray().toHex();
                c->insert(f, QString(blob.data()));
            }
            else
                c->insert(f, query.value(i).toString());
        }

        result.append(c);
    }

    return result;
}

QList<ChangeMap*> CDatabase::changedFields()
{
    QList<ChangeMap*> result;

    QSqlQuery query(QSqlDatabase::database());

    if(!query.exec(QString("SELECT `entry_id`,`date` FROM `changes` WHERE `entry_type`='%1' "
                           "AND (`action_type`='%2' OR `action_type`='%3')")
        .arg(this->Field).arg(this->Add).arg(this->Edit)))
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().databaseText());
        return result;
    }

    QDateTime last_sync = QDateTime::fromString(lastSync(), Qt::ISODate);
    QStringList fields;
    while(query.next())
    {
        QDateTime tmp = QDateTime::fromString(query.value(1).toString(), Qt::ISODate);
        if(tmp>=last_sync)
            fields.append(query.value(0).toString());
    }

    query.finish();

    if(!fields.count())
        return result;

    query.prepare("SELECT * FROM `fields` WHERE `id`='" + fields.join("' OR `id`='") + "'");
    if(!query.exec())
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().databaseText());
        return result;
    }

    while(query.next())
    {
        ChangeMap *field = new ChangeMap();

        QSqlRecord record = query.record();
        for(int i=0; i<record.count(); i++)
        {
            QString f = record.fieldName(i);
            if(f == "name" || f == "value")
            {
                QByteArray blob = query.value(i).toByteArray().toHex();
                field->insert(f, QString(blob.data()));
            }
            else
                field->insert(f, query.value(i).toString());
        }

        result.append(field);
    }

    return result;
}

QList<ChangeMap*> CDatabase::changedSections()
{
    QList<ChangeMap*> result;

    QSqlQuery query(QSqlDatabase::database());

    if(!query.exec(QString("SELECT `entry_id`,`date` FROM `changes` WHERE `entry_type`='%1' "
                           "AND (`action_type`='%2' OR `action_type`='%3')")
        .arg(this->Section).arg(this->Add).arg(this->Edit)))
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().databaseText());
        return result;
    }

    QDateTime last_sync = QDateTime::fromString(lastSync(), Qt::ISODate);
    QStringList fields;
    while(query.next())
    {
        QDateTime tmp = QDateTime::fromString(query.value(1).toString(), Qt::ISODate);
        if(tmp>=last_sync)
            fields.append(query.value(0).toString());
    }

    query.finish();

    if(!fields.count())
        return result;

    query.prepare("SELECT * FROM `sections` WHERE `id`='" + fields.join("' OR `id`='") + "'");
    if(!query.exec())
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().databaseText());
        return result;
    }

    while(query.next())
    {
        ChangeMap *field = new ChangeMap();

        QSqlRecord record = query.record();
        for(int i=0; i<record.count(); i++)
        {
            QString f = record.fieldName(i);
            if(f == "name" || f == "icon")
            {
                QByteArray blob = query.value(i).toByteArray().toHex();
                field->insert(f, QString(blob.data()));
            }
            else
                field->insert(f, query.value(i).toString());
        }

        result.append(field);
    }

    return result;
}

void CDatabase::setLastSync()
{
    QSqlQuery query(QSqlDatabase::database());
    query.prepare(QString("UPDATE `properties` SET "
                  "`value`='%1' WHERE `name`='last_sync'").arg(QDateTime::currentDateTime().toString(Qt::ISODate)));
    if(!query.exec())
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().databaseText());
        return;
    }
    query.finish();
}

QString CDatabase::lastSync()
{
    if(!open())
        return QString();

    QSqlQuery query(QSqlDatabase::database());
    query.prepare("SELECT `value` FROM `properties` WHERE `name`='last_sync'");
    if(query.exec() && query.first())
    {
        return query.value(0).toString();
    }

    return QString();
}

QMap<QString, QString> CDatabase::properties(QStringList list)
{
    QMap<QString, QString> result;
    if(!list.count())
        return result;

    QString querystr = "SELECT `name`,`value` FROM `properties` WHERE 1=0 ";
    foreach(QString str, list)
        querystr += "OR `name`='" + str + "' ";

    QSqlQuery query(QSqlDatabase::database());

    if(!query.exec(querystr))
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().databaseText());
        return result;
    }

    while(query.next())
    {
        result.insert(query.value(0).toString(), query.value(1).toString());
    }

    query.finish();

    return result;
}

bool CDatabase::importChanges(QByteArray &arr)
{
    QDomDocument doc;

    QString errorStr;
    int errorLine, errorColumn;
    if(!doc.setContent(QString::fromUtf8(arr), true, &errorStr, &errorLine,&errorColumn))
    {
        debug(QString("Parse error at line %1, column %2:\n%3")
            .arg(errorLine)
            .arg(errorColumn)
            .arg(errorStr));
        return false;
    }

    QDomElement root = doc.documentElement();
    if(root.tagName()!="passworder")
    {
        debug("Incorrect xml data");
        return false;
    }

    QDomNode node = root.firstChild();
    while(!node.isNull())
    {
        QString tagName = node.toElement().tagName();
        if(tagName=="changes")
            ;
        else if(tagName=="values")
        {
            QDomNode child = node.firstChild();
            while(!child.isNull())
            {
                if(!child.attributes().contains("type"))
                {
                    debug("Wrong node: " + child.toElement().tagName());
                    child = child.nextSibling();
                    continue;
                }
                QString type = child.attributes().namedItem("type").nodeValue();
                debug("Values type: " + type);
                processType(child, type);

                child = child.nextSibling();
            }
        }

        node = node.nextSibling();
    }


    return true;
}

bool CDatabase::insertValue(QDomNode &node, QString type)
{
    QMap<QString, QVariant> map;
    QDomNode child = node.firstChild();

    while(!child.isNull())
    {
        QDomNode val = child.firstChild();

        if(val.nodeValue().length())
            map.insert(child.nodeName(), val.nodeValue());

        child = child.nextSibling();
    }

    if(exists(map.value("id").toString(), type))
        return update(map, map.value("id").toString(), type, true);
    else
        return add(map, type, true);
}

bool CDatabase::processType(QDomNode &node, QString type)
{
    QDomNode child = node.firstChild();
    while(!child.isNull())
    {
        if(!insertValue(child, type))
        {
            debug("Can't process item by type: " + type);
            return false;
        }

        child = child.nextSibling();
    }

    return true;
}

bool CDatabase::exists(QString id, QString table)
{
    if(!open())
        return false;

    QSqlQuery query(QSqlDatabase::database());
    query.prepare("SELECT `id` FROM `" + table + "` WHERE `id`='" + id + "'");
    if(!query.exec())
    {
        debug("Query: " + query.lastQuery());
        debug("Can't exec query. Reason: " + QSqlDatabase::database().lastError().databaseText());
        return false;
    }

    return query.first();
}
